/**
 * Copyright ©2003~2019年4月3日 szkingdom
 * @author raohj
 * @date 2019年4月3日
 * @file RClassFile.java
 * @comment : please input your comment to this file
 * @version : input version
 * @modify:
 * 		1、 2019年4月3日 由 raohj 新建文件
 * 		2、此处解析仅仅是最基本的解析，后面发现有JAD开源库已经解决了Class解析的问题。
 */
package com.ccnu.demojava.jvm.classparse;

import com.ccnu.demojava.util.RConvert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileInputStream;
import java.io.IOException;

/**
 * @author raohj
 * @date 2019年4月3日
 * @version 
 * @comment to add your comment
 */
public class RClassParse {	
	static Logger log = LoggerFactory.getLogger(RClassParse.class);
	public static RClassStruct parseClassFile(String classFile) {
		RClassStruct rcf = null;
		FileInputStream fis = null;
		try {
			byte[] tmp1 = new byte[1];
			byte[] tmp2 = new byte[2];
			byte[] tmp4 = new byte[4];
			byte[] tmp8 = new byte[8];
			fis = new FileInputStream(classFile);
			rcf = new RClassStruct();
			
			fis.read(rcf.magic);			// 魔术数字
			fis.read(tmp2);	// 小版本号	
			rcf.minor_version = RConvert.bytesToShort(tmp2);
			fis.read(tmp2);	// 大版本号
			rcf.major_version = RConvert.bytesToShort(tmp2);
			
			fis.read(tmp2);
			rcf.constant_pool_count = RConvert.bytesToShort(tmp2);
			if(rcf.constant_pool_count<=1) {
				return null;
			}
			rcf.constant_pool = new RCS_cp_info[rcf.constant_pool_count];
			// 获取Constant_pool
			for(int i=0; i<rcf.constant_pool_count-1; i++) {
				RCS_cp_info cpInfo = new RCS_cp_info();
				rcf.constant_pool[i] = cpInfo;
				fis.read(tmp1);
				cpInfo.setTag(tmp1[0]);
				switch(cpInfo.getTag()) {
				case 1:	// CONSTANT_Utf8
					RCS_cp_info.Constant_Utf8_Info cutf8 = new RCS_cp_info.Constant_Utf8_Info();
					fis.read(tmp2);
					cutf8.len = RConvert.bytesToShort(tmp2);
					cutf8.bytes = new byte[cutf8.len];
					fis.read(cutf8.bytes);
					cpInfo.setInfo(cutf8);
					break;
				case 3:	// CONSTANT_Integer
					RCS_cp_info.Constant_Integer_Info ci = new RCS_cp_info.Constant_Integer_Info();
					fis.read(tmp4);
					ci.value = RConvert.bytesToInt(tmp4);
					cpInfo.setInfo(ci);
					break;				
				case 4:	// CONSTANT_Float
					RCS_cp_info.Constant_Float_Info cfloat = new RCS_cp_info.Constant_Float_Info();
					fis.read(tmp4);
					cfloat.value = RConvert.bytesToFloat(tmp4);
					cpInfo.setInfo(cfloat);
					break;		
				case 5:	// CONSTANT_Long
					RCS_cp_info.Constant_Long_Info cl = new RCS_cp_info.Constant_Long_Info();
					fis.read(tmp8);
					cl.value = RConvert.bytesToLong(tmp8);
					cpInfo.setInfo(cl);
					break;
				case 6:	// CONSTANT_Double
					RCS_cp_info.Constant_Double_Info cd = new RCS_cp_info.Constant_Double_Info();
					fis.read(tmp8);
					cd.value = RConvert.bytesToDouble(tmp8);
					cpInfo.setInfo(cd);					
					break;					
				case 7:	//CONSTANT_Class
					RCS_cp_info.Constant_Class_Info cc = new RCS_cp_info.Constant_Class_Info();
					fis.read(tmp2);
					cc.name_index = RConvert.bytesToShort(tmp2);
					cpInfo.setInfo(cc);
					break;
				case 8:	// CONSTANT_String
					RCS_cp_info.Constant_String_Info cs = new RCS_cp_info.Constant_String_Info();
					fis.read(tmp2);
					cs.string_index = RConvert.bytesToShort(tmp2);
					cpInfo.setInfo(cs);
					break;					
				case 9: // CONSTANT_Fieldref
					RCS_cp_info.Constant_Fieldref_Info cf = new RCS_cp_info.Constant_Fieldref_Info();
					fis.read(tmp2);
					cf.class_index = RConvert.bytesToShort(tmp2);
					fis.read(tmp2);
					cf.name_and_type_index = RConvert.bytesToShort(tmp2);
					cpInfo.setInfo(cf);
					break;
				case 10: // CONSTANT_Methodref
					RCS_cp_info.Constant_Methodref_Info cm = new RCS_cp_info.Constant_Methodref_Info();
					fis.read(tmp2);
					cm.class_index = RConvert.bytesToShort(tmp2);
					fis.read(tmp2);
					cm.name_and_type_index = RConvert.bytesToShort(tmp2);
					cpInfo.setInfo(cm);				
					break;					
				case 11: // CONSTANT_InterfaceMethodref
					RCS_cp_info.Constant_InterfaceMethodref_Info cim = new RCS_cp_info.Constant_InterfaceMethodref_Info();
					fis.read(tmp2);
					cim.class_index = RConvert.bytesToShort(tmp2);
					fis.read(tmp2);
					cim.name_and_type_index = RConvert.bytesToShort(tmp2);
					cpInfo.setInfo(cim);					
					break;		
				case 12: // CONSTANT_NameAndType
					RCS_cp_info.Constant_NameAndType_Info cnt = new RCS_cp_info.Constant_NameAndType_Info();
					fis.read(tmp2);
					cnt.name_index = RConvert.bytesToShort(tmp2);
					fis.read(tmp2);				
					cnt.descriptor_index = RConvert.bytesToShort(tmp2);
					cpInfo.setInfo(cnt);
					break;			
				case 15: // CONSTANT_MethodHandle
					RCS_cp_info.Constant_MethodHandle_Info cmh = new RCS_cp_info.Constant_MethodHandle_Info();
					fis.read(tmp1);
					cmh.reference_kind = tmp1[0];
					fis.read(tmp2);
					cmh.reference_index = RConvert.bytesToShort(tmp2);
					cpInfo.setInfo(cmh);
					break;			
				case 16: // CONSTANT_MethodType
					RCS_cp_info.Constant_MethodType_Info cmt = new RCS_cp_info.Constant_MethodType_Info();
					fis.read(tmp2);
					cmt.descriptor_index = RConvert.bytesToShort(tmp2);
					cpInfo.setInfo(cmt);					
					break;
				case 17: // CONSTANT_Dynamic
					RCS_cp_info.CONSTANT_Dynamic_info cdc = new RCS_cp_info.CONSTANT_Dynamic_info();
					fis.read(tmp2);
					cdc.bootstrap_method_attr_index = RConvert.bytesToShort(tmp2);
					fis.read(tmp2);
					cdc.name_and_type_index = RConvert.bytesToShort(tmp2);
					cpInfo.setInfo(cdc);					
					break;					
				case 18: // CONSTANT_InvokeDynamic
					RCS_cp_info.Constant_InvokeDynamic_Info cii = new RCS_cp_info.Constant_InvokeDynamic_Info();
					fis.read(tmp2);
					cii.bootstrap_method_attr_index = RConvert.bytesToShort(tmp2);
					fis.read(tmp2);
					cii.name_and_type_index = RConvert.bytesToShort(tmp2);
					cpInfo.setInfo(cii);					
					break;	
				case 19: // CONSTANT_InvokeDynamic
					RCS_cp_info.CONSTANT_Module_info cmi = new RCS_cp_info.CONSTANT_Module_info();
					fis.read(tmp2);
					cmi.name_index = RConvert.bytesToShort(tmp2);
					cpInfo.setInfo(cmi);					
					break;
				case 20: // CONSTANT_InvokeDynamic
					RCS_cp_info.CONSTANT_Package_info cpi = new RCS_cp_info.CONSTANT_Package_info();
					fis.read(tmp2);
					cpi.name_index = RConvert.bytesToShort(tmp2);
					cpInfo.setInfo(cpi);					
					break;					
				default:
					log.info("不支持的类型" + cpInfo.getTag());
					break;
				}
			}
			// 获取access_flag
			fis.read(rcf.access_flags);
			
			// 获取this_class
			fis.read(tmp2);
			rcf.this_class = RConvert.bytesToShort(tmp2);
			
			// 获取super_class
			fis.read(tmp2);
			rcf.super_class = RConvert.bytesToShort(tmp2);
			
			// interface_count
			fis.read(tmp2);
			rcf.interface_count = RConvert.bytesToShort(tmp2);
			if(rcf.interface_count>0) {
				rcf.interfaces = new short[rcf.interface_count];
				for(int i=0; i<rcf.interface_count; i++) {
					fis.read(tmp2);
					rcf.interfaces[i] = RConvert.bytesToShort(tmp2);
				}
			}
			// fields_count
			fis.read(tmp2);
			rcf.fields_count = RConvert.bytesToShort(tmp2);
			if(rcf.fields_count>0) {
				rcf.fields = new RCS_field_info[rcf.fields_count];
				for(int i=0; i<rcf.fields_count; i++) {
					RCS_field_info cfi = new RCS_field_info();
					rcf.fields[i] = cfi;
					fis.read(tmp2);
					cfi.access_flag = RConvert.bytesToShort(tmp2);
					fis.read(tmp2);
					cfi.name_index = RConvert.bytesToShort(tmp2);
					fis.read(tmp2);
					cfi.descriptor_index = RConvert.bytesToShort(tmp2);
					fis.read(tmp2);
					cfi.attr_count = RConvert.bytesToShort(tmp2);
					if(cfi.attr_count>0) {
						cfi.attributes = new RCS_attribute_info[cfi.attr_count];
						for(int j=0; j<cfi.attr_count; j++) {
							cfi.attributes[j] = new RCS_attribute_info();
							fis.read(tmp2);
							cfi.attributes[j].attr_name_index = RConvert.bytesToShort(tmp2);
							
							fis.read(tmp4);
							cfi.attributes[j].attr_length = RConvert.bytesToInt(tmp4);
							
							if(cfi.attributes[j].attr_length>0) {
								cfi.attributes[j].info = new byte[cfi.attributes[j].attr_length];
								fis.read(cfi.attributes[j].info);
							}
						}
					}
				}
			}
			
			// methods_count
			fis.read(tmp2);
			rcf.methods_count = RConvert.bytesToShort(tmp2);
			if(rcf.methods_count>0) {
				rcf.methods = new RCS_method_info[rcf.methods_count];
				for(int i=0; i<rcf.methods_count; i++) {
					RCS_method_info cmi = new RCS_method_info();
					rcf.methods[i] = cmi;					
					fis.read(cmi.access_flag);
					fis.read(tmp2);
					cmi.name_index = RConvert.bytesToShort(tmp2);
					fis.read(tmp2);
					cmi.descriptor_index = RConvert.bytesToShort(tmp2);
					fis.read(tmp2);
					cmi.attr_count = RConvert.bytesToShort(tmp2);
					if(cmi.attr_count>0) {
						cmi.attributes = new RCS_attribute_info[cmi.attr_count];
						for(int j=0; j<cmi.attr_count; j++) {
							cmi.attributes[j] = new RCS_attribute_info();
							fis.read(tmp2);
							cmi.attributes[j].attr_name_index = RConvert.bytesToShort(tmp2);
							
							fis.read(tmp4);
							cmi.attributes[j].attr_length = RConvert.bytesToInt(tmp4);
							
							if(cmi.attributes[j].attr_length>0) {
								cmi.attributes[j].info = new byte[cmi.attributes[j].attr_length];
								fis.read(cmi.attributes[j].info);
							}
						}
					}
				}
			}
			
			// attrbute_count
			fis.read(tmp2);
			rcf.attributes_count = RConvert.bytesToShort(tmp2);
			if(rcf.attributes_count>0) {
				rcf.attributes = new RCS_attribute_info[rcf.attributes_count];
				for(int j=0; j<rcf.attributes_count; j++) {
					rcf.attributes[j] = new RCS_attribute_info();
					fis.read(tmp2);
					rcf.attributes[j].attr_name_index = RConvert.bytesToShort(tmp2);
					
					fis.read(tmp4);
					rcf.attributes[j].attr_length = RConvert.bytesToInt(tmp4);
					
					if(rcf.attributes[j].attr_length>0) {
						rcf.attributes[j].info = new byte[rcf.attributes[j].attr_length];
						fis.read(rcf.attributes[j].info);
					}
				}
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		} finally {
			if(fis!=null) {
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return rcf;
	}
}
